package com.gitee.feizns.quickstart.web.utils;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.UUID;

/**
 * web环境下常用的和web环境相关的工具类
 * @author feizns
 * @since 2019/4/12
 */
public abstract class WebUtils {

    /**
     * 所有需要处理的代理请求头
     */
    private static final String[] IP_PROXY_HEADERS = { "x-forwarded-for", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR", "X-Real-IP" };

    /**
     * 获取用户真实ip
     * @link <a href="https://www.cnblogs.com/xiaoxing/p/6565573.html">JavaWeb获取客户端真实IP</a>
     * @return 返回请求用户的ip地址
     */
    public static String getIpAddress() {
        HttpServletRequest req = getReq();
        for (String header : IP_PROXY_HEADERS) {
            String val = req.getHeader(header);
            if ( ipValidate(val) )
                return val.split(",")[0];
        }
        return req.getRemoteAddr();
    }

    /**
     * 验证ip是否有效
     * @param val ip地址
     * @return 是否有效
     */
    private static boolean ipValidate(String val) {
        return val != null
                && val.length() != 0
                && !"unknown".equalsIgnoreCase(val);
    }

    /**
     * 获取当前web线程请求的请求头
     * <pre>{@code
     *  //在web环境下直接获取请求头
     *  System.out.println(WebUtils.getReqHeader("Accept-Encoding"));  //gzip, deflate, br
     * }</pre>
     * @param header 请求头
     * @return 返回该请求头的值
     */
    public static String getReqHeader(String header) {
        return getReq().getHeader(header);
    }

    /**
     * 设置当前web线程请求的相应头
     * <pre>{@code
     *  //下面两种用法相同
     *  WebUtils.setRespHeader("Content-Type", ""application/json);
     *  WebUtils.getResp().setHeader("Content-Type", "application/json");
     * }</pre>
     * @see #getResp()
     * @param name 响应头名称
     * @param val 对应响应头的值
     */
    public static void setRespHeader(String name, String val) {
        getResp().setHeader(name, val);
    }

    /**
     * 获取 UserAgent 客户端浏览器信息
     * <pre>{@code
     *  System.out.println(WebUtils.getUserAgent()); //Mozilla/5.0 (iPad; CPU OS 13_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/87.0.4280.77 Mobile/15E148 Safari/604.1
     * }</pre>
     * @see #getReq()
     * @return 返回客户端信息（可以配合工具分析使用）
     */
    public static String getUserAgent() {
        return getReq().getHeader("User-Agent");
    }

    /**
     * 当前web线程的 request 对象
     * （在异步环境下将无法获取。）
     * @see HttpServletRequest
     * @see RequestContextHolder
     * @return javax.servlet.http.HttpServletRequest
     */
    public static HttpServletRequest getReq() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }

    /**
     * 当前web线程的 response 对象
     * （在异步环境下将无法获取。）
     * @see HttpServletResponse
     * @see RequestContextHolder
     * @return javax.servlet.http.HttpServletRequest
     */
    public static HttpServletResponse getResp() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse();
    }

    /**
     * 获取当前服务器完整地址
     * <p>
     *     （scheme://host:port/context-path）
     *     协议://主机名:端口/服务器路径
     * </p>
     * @return 返回服务器完整地址
     */
    public static String getBasePath() {
        HttpServletRequest req = getReq();
        return getServerName() + req.getContextPath();
    }

    /**
     * 获取当前服务器域名和端口
     * <p>
     *     （scheme://host:port）
     *     协议://主机名:端口
     * </p>
     * @return 获取当前服务器域名和端口
     */
    public static String getServerName() {
        HttpServletRequest req = getReq();
        String scheme = req.getScheme();
        String port = "";
        int serverPort = req.getServerPort();
        if ( serverPort == 443 )
            scheme = "https";
        if ( serverPort != 80 && serverPort != 443 )
            port = ":" + serverPort;
        return scheme + "://"
                + req.getServerName()
                + port;
    }

    /**
     * 设置响应头为文件并设置文件名
     * @param filename {@link String}
     * @return {@link HttpServletResponse}
     */
    public static HttpServletResponse setFileHeaderToResp(String filename) {
        HttpServletResponse resp = WebUtils.getResp();
        resp.setContentType("application/octet-stream");
        resp.setHeader("Content-Disposition", String.format("attachment;filename=%s", filename));
        return resp;
    }

    /**
     * 随机生成文件名称并使用指定的文件后缀
     * @param suffix 文件后缀
     * @return {@link HttpServletResponse}
     */
    public static HttpServletResponse setFileHeaderToRespWithSuffix(String suffix) {
        return setFileHeaderToResp(UUID.randomUUID() + suffix);
    }

}
